package editor.widgets;

import editor.code.LineRenderer;
import lang.asm.DataTypeUtil;
import model.code.Function;
import model.code.GlobalVariable;
import model.code.Structure;
import model.code.line.Immediate;
import model.code.line.SingleLine;
import model.code.line.SyntaxError;
import model.symbol.*;

import javax.swing.*;
import java.awt.*;

/**
 * This class is for tooltip popups.  It has special cases for {@link SyntaxError}s and {@link Symbol}s
 *
 * @author Neil Dickson
 */
public class ToolTip extends JComponent {
	private String[]	text;

	public static final Color	SYMBOL_BG_COLOUR = new Color(0xFFD0A0);//new Color(0x80FFA000,true);
	public static final Color	ERROR_BG_COLOUR = new Color(0xFFA0A0);
	public static final Color	ERROR_COLOUR = new Color(0x000000);
	public static final Font	ERROR_FONT = new Font("Calibri",0,12);

	public ToolTip(Symbol symbol) {
		text = symbol.getDescription().toString().split("\n");
		if (text.length==0 || (text.length==1 && text[0].equals(""))) {
			text = new String[]{"(no comment)"};
		}
		// Special case for showing the value of a constant if it has one
		if (symbol instanceof Constant) {
			String[] newText = new String[text.length+1];
			System.arraycopy(text,0,newText,0,text.length);
			text = newText;
			Immediate immediate = ((Constant)symbol).getImmediate();
			if (immediate==null) {
				text[text.length-1] = "(value unable to be calculated)";
			}
			else {
				if (immediate.isDouble) {
					text[text.length-1] = "="+Double.longBitsToDouble(immediate.constant);
				}
				else {
					text[text.length-1] = "="+immediate.constant+" ("+Long.toHexString(immediate.constant)+"h)";
				}
				if (immediate.relocation!=null) {
					text[text.length-1] += " + address";
				}
				if (immediate.diffRelocation!=null) {
					text[text.length-1] += " + distance in bytes";
				}
			}
		}
		else if (symbol instanceof Function) {
			Function function = (Function)symbol;
			String[] newText = new String[text.length+1];
			System.arraycopy(text,0,newText,0,text.length);
			text = newText;
			String returnType = "";
			if (((Function)symbol).getReturns().isEmpty()) {
				returnType = "void";
			}
			else {
				Function.Return ret = ((Function)symbol).getReturns().get(0);
				SingleLine type = ret.getType();
				if (type.getNumReferences()==1 && type.getReferences()[0] instanceof Register) {
					int size = DataTypeUtil.TYPE_MAP[((Register)type.getReferences()[0]).getType()].size;
					if (size==128) {
						returnType = "XMMWORD";
					}
					else if (size==64) {
						returnType = "QWORD";
					}
					else if (size==32) {
						returnType = "DWORD";
					}
					else if (size==16) {
						returnType = "WORD";
					}
				}
				else {
					returnType = type.toString();
				}
			}
			StringBuffer buf = new StringBuffer(returnType);
			buf.append(" ").append(function.getName()).append("(");
			for (LocalVariable param : function.getParameters()) {
				buf.append(param.getType()).append(" ").append(param.getName()).append(",");
			}
			if (buf.charAt(buf.length()-1)==',') {
				buf.setCharAt(buf.length()-1,')');
			}
			else {
				buf.append(")");
			}
			text[text.length-1] = buf.toString();
		}
		else if (symbol instanceof Structure) {
			Structure structure = (Structure)symbol;
			String[] newText = new String[text.length+2];
			System.arraycopy(text,0,newText,0,text.length);
			text = newText;
			text[text.length-2] = "size: "+structure.getSize()+" bytes";
			StringBuffer buf = new StringBuffer("{");
			for (StructureMember member : structure.getMembers()) {
				buf.append(member.getName()).append(",");
			}
			if (buf.charAt(buf.length()-1)==',') {
				buf.setCharAt(buf.length()-1,'}');
			}
			else {
				buf.append("}");
			}
			text[text.length-1] = buf.toString();
		}
		else if (symbol instanceof GlobalVariable) {
			GlobalVariable variable = (GlobalVariable)symbol;
			String[] newText = new String[text.length+1+variable.getPieces().size()];
			System.arraycopy(text,0,newText,0,text.length);
			int i = text.length;
			text = newText;
			text[i++] = "size: "+variable.getSize()+" bytes";
			for (GlobalVariable.Piece piece : variable.getPieces()) {
				text[i++] = piece.getType()+" "+piece.getInitialValue();
			}
		}
		else if (symbol instanceof LocalVariable) {
			LocalVariable variable = (LocalVariable)symbol;
			String[] newText = new String[text.length+2];
			System.arraycopy(text,0,newText,0,text.length);
			int i = text.length;
			text = newText;
			text[i++] = "size: "+variable.getTypeSize()+" bytes";
			text[i] = variable.getType().toString();
		}
		else if (symbol instanceof StructureMember) {
			StructureMember member = (StructureMember)symbol;
			String[] newText = new String[text.length+2];
			System.arraycopy(text,0,newText,0,text.length);
			int i = text.length;
			text = newText;
			text[i++] = "size: "+member.getTotalSize()+" bytes; offset: "+member.getParent().getMemberOffset(member)+" bytes";
			text[i] = member.getType().toString()+" "+member.getInitialValue();
		}
		setOpaque(false);
		setFont(LineRenderer.getObjectFont(symbol));
		setForeground(LineRenderer.getObjectColour(symbol));
		setBackground(SYMBOL_BG_COLOUR);
		int maxWidth = 0;
		for (String line : text) {
			maxWidth = Math.max(maxWidth,getFontMetrics(getFont()).stringWidth(line));
		}
		setSize(maxWidth+2,((int)(getFont().getSize()*1.3))*text.length+2);
	}

	public ToolTip(SyntaxError error) {
		text = error.getDescription().split("\n");
		setOpaque(false);
		setFont(ERROR_FONT);
		setForeground(ERROR_COLOUR);
		setBackground(ERROR_BG_COLOUR);
		int maxWidth = 0;
		for (String line : text) {
			maxWidth = Math.max(maxWidth,getFontMetrics(getFont()).stringWidth(line));
		}
		setSize(maxWidth+2,((int)(getFont().getSize()*1.3))*text.length+2);
	}

	protected void paintComponent(Graphics g) {
		g.setColor(getBackground());
		g.fillRect(0,0,getWidth(),getHeight());
		g.setColor(getForeground());
		g.setFont(getFont());
		int y = getHeight()-getFont().getSize()/4-1 - ((int)(getFont().getSize()*1.3))*(text.length-1);
		for (String line : text) {
			g.drawString(line,2,y);
			y += (int)(getFont().getSize()*1.3);
		}
	}
}
